import easyquotation
from typing import Dict, List, Optional
import yaml
import time
import os
import logging
from pathlib import Path
from utils.price_formatter import format_price, format_change_percent, format_change_amount

# 尝试导入yfinance数据源
try:
    from .yfinance_source import create_yfinance_quotation
    YFINANCE_AVAILABLE = True
except ImportError:
    YFINANCE_AVAILABLE = False

class AStockMonitor:
    """A股行情监控模块"""
    
    def __init__(self, config_path: str = 'config/stocks.yaml', data_source: str = None):
        """初始化A股行情监控器
        
        Args:
            config_path: 配置文件路径
            data_source: 数据源 ('sina', 'tencent', 'yfinance', 'auto')，会覆盖配置文件中的设置
        """
        self.config_path = config_path
        self.quotation = None
        self.stocks = []
        self.settings = {}
        self.override_data_source = data_source  # 命令行指定的数据源
        self.auto_switch_enabled = data_source == 'auto'  # 是否启用自动切换
        self.available_sources = ['sina', 'tencent']  # 可用的数据源列表
        if YFINANCE_AVAILABLE:
            self.available_sources.append('yfinance')
        self._setup_logging()
        self._initialize()
    
    def _setup_logging(self):
        """Setup logging configuration"""
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s',
            handlers=[
                logging.StreamHandler(),
                logging.FileHandler('stock_quotes.log')
            ]
        )
        self.logger = logging.getLogger(__name__)
    
    def _setup_quotation_service(self):
        """Setup quotation service based on configuration"""
        # 优先使用命令行指定的数据源，否则使用配置文件中的
        if self.override_data_source:
            if self.override_data_source == 'auto':
                # auto模式下使用默认的第一个数据源
                data_source = self.available_sources[0]
                self.logger.info(f"Using auto mode, starting with data source: {data_source}")
            else:
                data_source = self.override_data_source
                self.logger.info(f"Using command line data source: {data_source}")
        else:
            data_source = self.settings.get('data_source', 'tencent')
            self.logger.info(f"Using config file data source: {data_source}")
        
        self.quotation = self._create_quotation_service(data_source)
        self.current_data_source = data_source
    
    def _create_quotation_service(self, source: str):
        """Create quotation service based on source type"""
        try:
            # Normalize source name
            source = source.lower().strip()
            
            # Map source names to quotation services
            if source in ['sina', '新浪']:
                return easyquotation.use('sina')
            elif source in ['tencent', 'qq', '腾讯']:
                return easyquotation.use('tencent')
            elif source in ['yfinance', 'yahoo', 'yf']:
                if YFINANCE_AVAILABLE:
                    self.logger.info("Using YFinance data source")
                    return create_yfinance_quotation()
                else:
                    self.logger.error("YFinance not available, please install yfinance: pip install yfinance")
                    raise ImportError("YFinance not available")
            else:
                self.logger.warning(f"Unknown data source '{source}', using default 'tencent'")
                return easyquotation.use('tencent')
                
        except Exception as e:
            self.logger.error(f"Failed to create quotation service with source '{source}': {e}")
            self.logger.info(f"Fallback to default 'sina' data source")
            return easyquotation.use('sina')
    
    def _initialize(self):
        """Initialize quotation service and load config"""
        try:
            # Load configuration first
            config = self._load_config()
            self.stocks = config.get('a_stocks', [])
            self.settings = config.get('settings', {})
            
            # Initialize quotation service with configured data source
            self._setup_quotation_service()
            
            self.logger.info(f"Successfully initialized with {len(self.stocks)} A-stocks")
        except Exception as e:
            self.logger.error(f"Initialization failed: {e}")
            raise
    
    def _load_config(self) -> Dict:
        """Load A-stock configuration with validation"""
        try:
            if not os.path.exists(self.config_path):
                raise FileNotFoundError(f"Config file not found: {self.config_path}")
            
            with open(self.config_path, 'r', encoding='utf-8') as f:
                config = yaml.safe_load(f)
            
            # Validate and process A-stocks
            a_stocks = config.get('a_stocks', [])
            validated_stocks = []
            for stock in a_stocks:
                stock_code = str(stock).strip()
                if len(stock_code) == 6 and stock_code.isdigit():
                    validated_stocks.append(stock_code)
                else:
                    self.logger.warning(f"Invalid A-stock code format: {stock_code}")
            
            # Update config with validated stocks
            config['a_stocks'] = validated_stocks
            
            return config
            
        except Exception as e:
            self.logger.error(f"Failed to load config: {e}")
            raise
    
    def get_real_time_quotes(self) -> Optional[Dict]:
        """Get real-time A-stock quotes with error handling and fallback"""
        try:
            if not self.quotation or not self.stocks:
                return None
            
            # 如果启用了auto模式，使用单个股票级别的智能获取
            if self.auto_switch_enabled:
                return self._get_quotes_with_auto_switch()
            
            # 传统模式：批量获取，失败时整体切换数据源
            quotes = self.quotation.stocks(self.stocks)
            
            if not quotes:
                self.logger.warning("No quote data received for A-stocks")
                # 尝试智能切换数据源
                if self._try_fallback_data_source():
                    quotes = self.quotation.stocks(self.stocks)
                
                if not quotes:
                    return None
            
            return quotes
            
        except Exception as e:
            self.logger.error(f"Failed to fetch A-stock quotes: {e}")
            # 尝试智能切换数据源
            if self._try_fallback_data_source():
                try:
                    quotes = self.quotation.stocks(self.stocks)
                    return quotes
                except Exception as fallback_e:
                    self.logger.error(f"Fallback data source also failed: {fallback_e}")
            return None
    
    def get_quotes(self) -> Optional[Dict]:
        """Get A-stock quotes"""
        return self.get_real_time_quotes()
    
    def switch_data_source(self, source: str) -> bool:
        """Switch data source
        
        Args:
            source: 'sina' or 'tencent'/'qq'
        
        Returns:
            bool: True if switch successful, False otherwise
        """
        try:
            self.quotation = self._create_quotation_service(source)
            # Update settings
            self.settings['data_source'] = source
            self.logger.info(f"Switched data source to: {source}")
            return True
                
        except Exception as e:
            self.logger.error(f"Failed to switch data source to {source}: {e}")
            return False
    
    def get_current_data_source(self) -> str:
        """Get current data source"""
        return getattr(self, 'current_data_source', self.settings.get('data_source', 'tencent'))
    
    def _try_fallback_data_source(self) -> bool:
        """尝试切换到备用数据源
        
        Returns:
            bool: 切换成功返回True，失败返回False
        """
        current_source = self.get_current_data_source()
        
        # 定义数据源优先级顺序
        fallback_sources = []
        if current_source == 'sina':
            fallback_sources = ['tencent', 'yfinance']
        elif current_source == 'tencent':
            fallback_sources = ['sina', 'yfinance']
        elif current_source == 'yfinance':
            fallback_sources = ['sina', 'tencent']
        else:
            fallback_sources = ['sina', 'tencent', 'yfinance']
        
        # 尝试每个备用数据源
        for fallback_source in fallback_sources:
            try:
                self.logger.info(f"Trying fallback data source: {fallback_source}")
                
                # 跳过不可用的yfinance
                if fallback_source == 'yfinance' and not YFINANCE_AVAILABLE:
                    continue
                
                # 创建新的quotation服务
                new_quotation = self._create_quotation_service(fallback_source)
                
                # 测试新数据源
                if self._test_quotation_service(new_quotation):
                    self.quotation = new_quotation
                    self.current_data_source = fallback_source
                    self.logger.info(f"Successfully switched to fallback data source: {fallback_source}")
                    return True
                else:
                    self.logger.warning(f"Fallback data source {fallback_source} test failed")
                    
            except Exception as e:
                self.logger.error(f"Failed to switch to fallback data source {fallback_source}: {e}")
                continue
        
        self.logger.error("All fallback data sources failed")
        return False
    
    def _get_quotes_with_auto_switch(self) -> Optional[Dict]:
        """使用自动切换模式获取股票数据，单个股票级别的智能切换
        
        Returns:
            Dict: 成功获取的股票数据字典
        """
        if not self.stocks:
            return None
        
        all_quotes = {}
        failed_stocks = []
        
        self.logger.info(f"Auto-switch mode: fetching {len(self.stocks)} stocks individually")
        
        # 为每个股票尝试获取数据
        for stock_code in self.stocks:
            quote_data = self._get_single_stock_with_fallback(stock_code)
            if quote_data:
                all_quotes.update(quote_data)
            else:
                failed_stocks.append(stock_code)
        
        # 统计结果
        success_count = len(all_quotes)
        total_count = len(self.stocks)
        success_rate = (success_count / total_count * 100) if total_count > 0 else 0
        
        self.logger.info(f"Auto-switch fetch completed: {success_count}/{total_count} ({success_rate:.1f}%)")
        
        if failed_stocks:
            self.logger.warning(f"Failed to fetch data for stocks: {failed_stocks}")
        
        return all_quotes if all_quotes else None
    
    def _get_single_stock_with_fallback(self, stock_code: str) -> Optional[Dict]:
        """为单个股票尝试多个数据源获取数据
        
        Args:
            stock_code: 股票代码
            
        Returns:
            Dict: 成功获取的股票数据，失败返回None
        """
        # 按优先级尝试每个数据源
        for source in self.available_sources:
            try:
                # 创建该数据源的quotation服务
                quotation_service = self._create_quotation_service(source)
                
                # 尝试获取单个股票数据
                quote_data = quotation_service.stocks([stock_code])
                
                # 验证数据有效性
                if quote_data and stock_code in quote_data:
                    stock_data = quote_data[stock_code]
                    if isinstance(stock_data, dict) and stock_data.get('now', 0) > 0:
                        self.logger.debug(f"Stock {stock_code} fetched successfully from {source}")
                        return quote_data
                
                self.logger.debug(f"Stock {stock_code} failed from {source}: no valid data")
                
            except Exception as e:
                self.logger.debug(f"Stock {stock_code} failed from {source}: {e}")
                continue
        
        self.logger.warning(f"Stock {stock_code} failed from all data sources")
        return None
    
    def _test_quotation_service(self, quotation_service) -> bool:
        """测试quotation服务是否可用
        
        Args:
            quotation_service: 要测试的quotation服务
            
        Returns:
            bool: 可用返回True，不可用返回False
        """
        try:
            if not self.stocks:
                return False
            
            # 使用第一只股票进行测试
            test_stocks = [self.stocks[0]]
            test_quotes = quotation_service.stocks(test_stocks)
            
            # 检查是否有有效数据
            if test_quotes and len(test_quotes) > 0:
                for code, data in test_quotes.items():
                    if isinstance(data, dict) and data.get('now', 0) > 0:
                        return True
            
            return False
            
        except Exception as e:
            self.logger.error(f"Quotation service test failed: {e}")
            return False
    
    def test_connection(self) -> bool:
        """测试数据源连接
        
        Returns:
            bool: 连接成功返回True，失败返回False
        """
        try:
            if not self.quotation or not self.stocks:
                self.logger.error("Quotation service or stocks not initialized")
                return False
            
            # 获取第一只股票的数据进行测试
            test_stocks = [self.stocks[0]] if self.stocks else []
            quotes = self.quotation.stocks(test_stocks)
            
            if quotes:
                self.logger.info(f"Connection test successful, got data for {len(quotes)} stocks")
                return True
            else:
                self.logger.warning("Connection test failed - no data received")
                return False
                
        except Exception as e:
            self.logger.error(f"Connection test failed: {e}")
            return False
    
    def display_quotes(self, quotes: Dict):
        """公共方法：显示股票行情数据"""
        self._display_quotes(quotes)
    
    def start_monitoring(self, interval: int = 5):
        """启动行情监控
        
        Args:
            interval: 刷新间隔(秒)
        """
        self.monitor(interval)
    

    
    def monitor(self, interval: Optional[int] = None):
        """Monitor A-stock quotes continuously"""
        if interval is None:
            interval = self.settings.get('refresh_interval', 5)
        
        if interval < 1:
            interval = 1
            self.logger.warning("Interval too small, set to 1 second")
        
        current_source = self.get_current_data_source()
        print(f"📊 A股行情监控已启动")
        print(f"📈 监控股票: {len(self.stocks)} 只")
        print(f"🔄 刷新间隔: {interval} 秒")
        print(f"📡 数据源: {current_source}")
        print(f"⏰ 开始时间: {time.strftime('%Y-%m-%d %H:%M:%S')}")
        print("按 Ctrl+C 停止监控\n")
        
        consecutive_errors = 0
        max_errors = 5
        
        while True:
            try:
                quotes = self.get_quotes()
                
                if quotes:
                    self._display_quotes(quotes)
                    consecutive_errors = 0  # Reset error counter on success
                else:
                    consecutive_errors += 1
                    print(f"Failed to get quotes (attempt {consecutive_errors}/{max_errors})")
                
                if consecutive_errors >= max_errors:
                    print("Too many consecutive errors, stopping...")
                    break
                
                time.sleep(interval)
                
            except KeyboardInterrupt:
                print("\nMonitoring stopped by user")
                self.logger.info("Monitoring stopped by user")
                break
            except Exception as e:
                consecutive_errors += 1
                self.logger.error(f"Monitoring error: {e}")
                print(f"Error: {e} (attempt {consecutive_errors}/{max_errors})")
                
                if consecutive_errors >= max_errors:
                    print("Too many consecutive errors, stopping...")
                    break
                
                time.sleep(interval)
    
    def _format_volume(self, volume: int) -> str:
        """Format volume for display"""
        if volume >= 100000000:  # 100M+
            return f"{volume/100000000:.1f}B"
        elif volume >= 1000000:  # 1M+
            return f"{volume/1000000:.1f}M"
        elif volume >= 1000:     # 1K+
            return f"{volume/1000:.1f}K"
        else:
            return str(volume)
    
    def _calculate_change(self, current: float, yesterday_close: float) -> tuple:
        """Calculate price change and percentage"""
        change_amount = current - yesterday_close
        change_percent = (change_amount / yesterday_close) * 100 if yesterday_close != 0 else 0
        return change_amount, change_percent
    
    def _display_quotes(self, quotes: Dict):
        """Display A-stock quotes with color coding and trend ranking"""
        if not quotes:
            return
        
        current_time = time.strftime('%Y-%m-%d %H:%M:%S')
        
        print(f"\n{'-'*100}")
        print(f"A股实时行情 - {current_time}")
        print(f"{'-'*100}")
        
        # 使用趋势排行功能
        try:
            from .stock_trend_ranker import create_trend_ranker
            ranker = create_trend_ranker()
            trends = ranker.analyze_trends(quotes)
            
            if trends:
                # 按涨跌幅排序显示
                sorted_trends = ranker.sort_by_trend(trends)
                
                print(f"\n📊 涨跌排行 (共{len(sorted_trends)}只股票):")
                for i, trend in enumerate(sorted_trends, 1):
                    try:
                        # 格式化价格显示（ETF显示3位小数，普通股票2位小数）
                        price_str = format_price(trend.current_price, stock_code=trend.code, stock_name=trend.name)
                        high_str = format_price(trend.high, stock_code=trend.code, stock_name=trend.name)
                        low_str = format_price(trend.low, stock_code=trend.code, stock_name=trend.name)
                        open_str = format_price(trend.open, stock_code=trend.code, stock_name=trend.name)
                        
                        # 格式化涨跌幅显示（带颜色和趋势符号，智能小数位数）
                        change_amount_str = format_change_amount(trend.change_amount, stock_code=trend.code, stock_name=trend.name)
                        change_percent_str = format_change_percent(trend.change_percent, stock_code=trend.code, stock_name=trend.name)
                        if trend.change_percent > 0:
                            change_str = f"{change_amount_str}({change_percent_str})"
                            colored_change = f"\033[31m📈{change_str}\033[0m"
                            rank_emoji = "🔥" if trend.change_percent > 5 else "📈"
                        elif trend.change_percent < 0:
                            change_str = f"{change_amount_str}({change_percent_str})"
                            colored_change = f"\033[32m📉{change_str}\033[0m"
                            rank_emoji = "💧" if trend.change_percent < -5 else "📉"
                        else:
                            change_str = f"{change_amount_str}({change_percent_str})"
                            colored_change = f"⚪{change_str}"
                            rank_emoji = "⚪"
                        
                        # 格式化成交量
                        volume_str = ranker._format_volume(trend.volume)
                        
                        # Display stock info with ranking
                        print(f"{rank_emoji}{i:2d}. {trend.name}({trend.code}) 现价:¥{price_str} "
                              f"涨跌:{colored_change} 量:{volume_str} "
                              f"高:¥{high_str} 低:¥{low_str} 开:¥{open_str}")
                        print("─" * 85)
                        
                    except (KeyError, ValueError, TypeError) as e:
                        self.logger.error(f"Error processing trend data for {trend.code}: {e}")
                        print(f"Error processing trend data for {trend.code}")
                
                # 显示市场概况
                summary = ranker.get_market_summary(trends)
                print(f"\n📈 市场概况:")
                print(f"   上涨: {summary['up_count']}只({summary['up_ratio']:.1f}%) | "
                      f"下跌: {summary['down_count']}只({summary['down_ratio']:.1f}%) | "
                      f"平盘: {summary['flat_count']}只")
                print(f"   平均涨跌: {summary['avg_change_percent']:+.2f}% | "
                      f"最大涨幅: {summary['max_gain']:+.2f}% | "
                      f"最大跌幅: {summary['max_loss']:+.2f}%")
            else:
                # 如果趋势分析失败，回退到原始显示方式
                self._display_quotes_fallback(quotes)
                
        except Exception as e:
            self.logger.warning(f"趋势排行功能异常，使用基础显示: {e}")
            self._display_quotes_fallback(quotes)
        
        print("\n说明: 涨跌 = 相对昨日收盘价变化 | 成交量: K=千, M=百万, B=十亿")
        print("趋势说明: 🔥强涨 📈上涨 ⚪平盘 📉下跌 💧重跌")
        print("颜色说明: \033[31m红色=上涨\033[0m, \033[32m绿色=下跌\033[0m")
    
    def _display_quotes_fallback(self, quotes: Dict):
        """原始显示方式（备用）"""
        for code, data in quotes.items():
            try:
                # Calculate change amount and percentage
                current_price = float(data['now'])
                yesterday_close = float(data['close'])
                change_amount, change_percent = self._calculate_change(current_price, yesterday_close)
                
                # Format prices with intelligent decimal places (ETF: 3, others: 2)
                stock_name = data.get('name', '')
                price_str = format_price(current_price, stock_code=code, stock_name=stock_name)
                high_str = format_price(data['high'], stock_code=code, stock_name=stock_name)
                low_str = format_price(data['low'], stock_code=code, stock_name=stock_name)
                open_str = format_price(data['open'], stock_code=code, stock_name=stock_name)
                
                # Format change display with color (intelligent decimal places)
                change_str = format_change_amount(change_amount, stock_code=code, stock_name=stock_name)
                change_percent_str = format_change_percent(change_percent, stock_code=code, stock_name=stock_name)
                
                # Apply color based on change percentage
                if change_percent > 0:
                    colored_change = f"\033[31m{change_str}({change_percent_str})\033[0m"
                elif change_percent < 0:
                    colored_change = f"\033[32m{change_str}({change_percent_str})\033[0m"
                else:
                    colored_change = f"{change_str}({change_percent_str})"
                
                # Format volume
                volume = int(data['turnover'])
                volume_str = self._format_volume(volume)
                
                # Display stock info in one line
                print(f"{data['name']}({code}) 现价:{price_str} "
                      f"涨跌:{colored_change} 量:{volume_str} "
                      f"高:{high_str} 低:{low_str} 开:{open_str}")
                print("─" * 85)
                
            except (KeyError, ValueError, TypeError) as e:
                self.logger.error(f"Error processing data for {code}: {e}")
                print(f"Error processing data for {code}")

# 模块级别的便捷函数，用于向后兼容
def create_monitor(config_path: str = 'config/stocks.yaml', data_source: str = None) -> AStockMonitor:
    """创建A股监控器实例
    
    Args:
        config_path: 配置文件路径
        data_source: 数据源
        
    Returns:
        AStockMonitor: 监控器实例
    """
    return AStockMonitor(config_path=config_path, data_source=data_source)


if __name__ == "__main__":
    # 直接运行此模块时的向后兼容行为
    print("⚠️  建议使用 'python main.py a' 来启动A股监控")
    print("📖 运行 'python main.py --help' 查看所有可用选项")
    
    # 提供基本的直接运行功能
    try:
        monitor = AStockMonitor()
        monitor.start_monitoring()
    except Exception as e:
        print(f"❌ 启动失败: {e}")
        exit(1)
